﻿#include "TimePluginMgr.h"
#include "boost/filesystem/path.hpp"
#include "boost/filesystem/operations.hpp"
#include <boost/locale.hpp>
// #include "QCoreApplication"
// #include "QDir"
// #include "QFileInfo"
// #include <QDebug>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

DllTimerPlugin::~DllTimerPlugin()
{
#if 0
	if (m_hmod != NULL) FreeLibrary(m_hmod);
#endif
}

DllTimerPlugin::DllTimerPlugin(const std::wstring& modname)
:TimerPlugin(), m_plugin(NULL)
{
	_name = L"NA";
	_err = L"NA";

	wstring filename = modname;
	
#if 0
	m_hmod = LoadLibrary(filename.c_str());
	if (m_hmod != NULL)
	{
		m_proc = (PluginProc)GetProcAddress(m_hmod, TIME_PLUGIN_ENTRY_NAME);
		if (m_proc != NULL)
		{
			import();
			if (m_plugin == NULL){
				_err = L"无法实例化插件 " + filename + L" 对象，接口版本不匹配";
			}
			else{
				_err = L"成功实例化插件" + filename;
			}
		}
		else{
			_err = L"无法定位插件 " + filename + L" 的入口函数";
		}
	}
	else{
		_err = L"无法载入插件 " + filename;
	}
#else

	std::string anSiNam = boost::locale::conv::utf_to_utf<char>(modname);  
	if (m_loader.Open(anSiNam))
	{
		m_proc = (PluginProc)m_loader.GetProc(TIME_PLUGIN_ENTRY_NAME);
		if (m_proc != NULL)
		{
			import();
			if (m_plugin == NULL)
			{
				_err = wstring(L"无法实例化插件:") + filename + wstring(L" 对象，接口版本不匹配");
			}
			else
			{
				_err = wstring(L"成功实例化插件:") + filename;
			}
		}
		else
		{
			_err = wstring(L"无法定位插件:") + filename + wstring(L" 的入口函数");
		}
	}
	else
	{
		_err = wstring(L"无法载入插件:") + filename + wstring(L",Detail:") + m_loader.WGetLastErrorMsg();
	}

#endif
}

void DllTimerPlugin::import()
{
	try
	{
		m_plugin = (TimerPlugin*)m_proc(TIME_PLUGIN_INTERFACE_VERSION);
	}
	catch (std::exception)
	{
		_err = wstring(L"插件执行过程中发生SEH异常");
	}
}

bool DllTimerPlugin::Inited()
{
	return m_plugin != NULL;
}
void DllTimerPlugin::Close()
{
	m_plugin->Close();
}
/*
*return 0, not rotated,1 rotated
*/
int DllTimerPlugin::GetTime(TimeDllGetTimeStruct* time_struct)
{
	m_plugin->GetTime(time_struct);
	//mirror(dest, column, line, DecoderPluginMgr::getInstance()->GetCurMirrorState());
	//return rotate(dest, column, line, DecoderPluginMgr::getInstance()->GetCurRotateState());
	return 0;
}

int DllTimerPlugin::GetTimeOffsetAndLength(TimeDllGetTimeStruct* offset_struct)
{
	return m_plugin->GetTimeOffsetAndLength(offset_struct);
}


const wchar_t* DllTimerPlugin::Name()
{
	//must ensure the return not NULL
	if (m_plugin->Name() == NULL){
		return _name.c_str();
	}
	return m_plugin->Name();
}
const wchar_t* DllTimerPlugin::ErrorInfo()
{
	if (m_plugin->ErrorInfo() == NULL)
		return _err.c_str();
	return m_plugin->ErrorInfo();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TimerPluginMgr* TimerPluginMgr::m_pInst = TimerPluginMgr::getInstance();
TimerPluginMgr::TimerPluginMgr()
{
	decoders.clear();
	
#ifdef _WIN32
	RescanPlugins("\\Timer\\", decoders);
#else
	RescanPlugins("/Timer", decoders);
#endif
	SetDefaultDecoder();
}

TimerPluginMgr::~TimerPluginMgr()
{
	for (std::map<wstring, TimerPlugin*>::iterator it = decoders.begin(); it != decoders.end(); it++){
		it->second->Close();
		delete it->second;
	}
}

void TimerPluginMgr::SetDefaultDecoder()
{
	bool find_default_decoder = false;
	bool find_default_read_format = false;
	for (std::map<wstring, TimerPlugin*>::iterator it = decoders.begin(); it != decoders.end(); it++){
		if (it->first == L"GENERAL-FORMAT"){
			current_decoder = default_decoder = it->first;
			find_default_decoder = true;
			break;
		}
	}
	if (decoders.size() > 0 && !find_default_decoder) {
		default_decoder = decoders.begin()->first;
	}

	return;
}
int TimerPluginMgr::RescanPlugins(string path, std::map<wstring, TimerPlugin*> &plugin_map)
{
// 	QDir dir(qApp->applicationDirPath() + "/Timer");
// 	
// 	dir.setFilter(QDir::Files);
// 	QStringList nameFilter = QStringList() << QString("").asprintf("*%s", DLL_SUFFIX);
// 	qDebug() << dir.dirName() << nameFilter;
// 	dir.setNameFilters(nameFilter);
// 	QFileInfoList fList = dir.entryInfoList();
// 	for (auto it : fList)
// 	{
// 		if (it.isFile())
// 		{
// 			DllTimerPlugin *decoder = new DllTimerPlugin(it.fileName().toStdWString());
// 			if (decoder && decoder->Inited())
// 			{
// 				plugin_map[decoder->Name()] = decoder;
// 				wcout << "[pluginmgr] " << decoder->Name() << endl;
// 			}
// 			else
// 			{
// 				delete decoder;
// 			}
// 		}
// 	}

#if 1
	string fullpath = "";
	
	try
	{
		fullpath =  boost::filesystem::initial_path<boost::filesystem::path>().string();
	}
	catch (...)
	{
		cout << "boost::filesystem::initial_path catch except " << endl;
		fullpath = ".";
	}
	fullpath += path;
	cout << "[pluginmgr] Decoder plugin path = " << fullpath << endl;
	boost::filesystem::path full_path(fullpath/*, boost::filesystem::native*/);
	if (boost::filesystem::exists(full_path))
	{
		for (boost::filesystem::directory_iterator item_begin(full_path); item_begin != boost::filesystem::directory_iterator(); item_begin++){
			if (boost::filesystem::is_regular_file(*item_begin))
			{
				wstring file_name = item_begin->path().wstring();
				if (file_name.find(DLL_SUFFIX) != std::string::npos)
				{
					wcout << "[pluginFileName] " << file_name << endl;
					DllTimerPlugin *decoder = new DllTimerPlugin(file_name);
					if (decoder && decoder->Inited())
					{
						plugin_map[decoder->Name()] = decoder;
						wcout << "[pluginLoadName] " << decoder->Name() << endl;
					}
					else
					{
						delete decoder;
					}
				}
			}
		}
	}

#endif
	
	return plugin_map.size();
}


bool GetRecordTimeInIndex(IN EagleData_Record_Id rec_id,
	IN EagleData_CcdRecord_Id ccd_rec_id,
	IN long frame_index,
	IN int column,
	IN int line,
	IN int color_depth,
	IN wstring decoder,
	OUT unsigned long* hour,
	OUT unsigned long* minite,
	OUT unsigned long* second,
	OUT unsigned long* millsecond)
{

	*hour = *minite = *second = *millsecond = 0;
	TimerPluginMgr* mgr = TimerPluginMgr::getInstance();

	TimerPlugin* time_plugin = mgr->getDecoder(decoder);
	TimeDllGetTimeStruct time_struct;
	memset(&time_struct, 0x00, sizeof(time_struct));
	time_struct.column = column;
	time_struct.line = line;
	time_struct.color_depth = color_depth;
	time_plugin->GetTimeOffsetAndLength(&time_struct);

	time_struct.buffer = new unsigned char[time_struct.length];
	memset(time_struct.buffer, 0x00, time_struct.length);

	unsigned long read_len = 0;

	while (read_len != time_struct.length)
	{
		int ret = EagleData_Read512InsideFrame(rec_id, ccd_rec_id, frame_index, (time_struct.read_offset + read_len) / 512, (char*)(time_struct.buffer + read_len));
		if (ret != 0)
			break;
		read_len += 512;
	}
	if (read_len != time_struct.length)
	{
		delete time_struct.buffer;
		return false;
	}

	time_plugin->GetTime(&time_struct);

	*hour = time_struct.hour % 24;
	*minite = time_struct.minite % 60;
	*second = time_struct.second % 60;
	*millsecond = time_struct.millsecond % 1000;
	delete time_struct.buffer;
	return true;
}


bool GetRecordTimeFromReadBuffer(IN unsigned char* frame_buffer,
	IN unsigned char* header,
	IN int column,
	IN int line,
	IN int color_depth,
	IN wstring decoder,
	OUT unsigned long* hour,
	OUT unsigned long* minite,
	OUT unsigned long* second,
	OUT unsigned long* millsecond)
{
	int head_size = HEAD_SIZE(column, line, color_depth);
	*hour = *minite = *second = *millsecond = 0;
	TimerPluginMgr* mgr = TimerPluginMgr::getInstance();

	TimeDllGetTimeStruct time_struct;
	memset(&time_struct, 0x00, sizeof(time_struct));
	time_struct.column = column;
	time_struct.line = line;
	time_struct.color_depth = color_depth;

	TimerPlugin* time_plugin = mgr->getDecoder(decoder);
	time_plugin->GetTimeOffsetAndLength(&time_struct);

	if (time_struct.read_offset >= head_size)
	{
		time_struct.buffer = frame_buffer + (time_struct.read_offset - head_size);
	}
	else
	{
		time_struct.buffer = header;
	}

	time_struct.buffer_decoded = true;
	
	time_plugin->GetTime(&time_struct);
	*hour = time_struct.hour % 24;
	*minite = time_struct.minite % 60;
	*second = time_struct.second % 60;
	*millsecond = time_struct.millsecond % 1000;
	return true;
}